// 播放器状态  0:未开始，1:播放中，2:暂停中，
// 解码器状态 0:空闲，1:初始化，2:准备就绪，3:已完成
// 下载器

interface VideoInfo {
  duration: number;
  size: number;
}
interface DataInfo {
  d: ArrayBuffer;
}

class Texture {
  private gl: WebGLRenderingContext;
  private texture: WebGLTexture | null;

  constructor(gl: WebGLRenderingContext) {
    this.gl = gl;

    this.texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, this.texture);

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  }

  bind(n: number, program: WebGLProgram, name: string) {
    this.gl.activeTexture(
      [this.gl.TEXTURE0, this.gl.TEXTURE1, this.gl.TEXTURE2][n]
    );
    this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
    this.gl.uniform1i(this.gl.getUniformLocation(program, name), n);
  }

  fill(width: number, height: number, data: Uint8Array) {
    this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
    this.gl.texImage2D(
      this.gl.TEXTURE_2D,
      0,
      this.gl.LUMINANCE,
      width,
      height,
      0,
      this.gl.LUMINANCE,
      this.gl.UNSIGNED_BYTE,
      data
    );
  }
}
class Player {
  private url: string; //视频地址
  private container: HTMLCanvasElement; //播放器容器
  private downloadWorker: Worker; //下载器
  private decodeWorker: Worker; //解码器
  private playerState: number = 0; //播放器状态
  private decodeState: number = 0; //解码器状态
  private duration: number = 0; //视频时长
  private decoding: boolean = false; //是否正在解码
  private step: number = 0;
  private height: number = 0;
  private width: number = 0;
  private yLength: number = 0;
  private uvLength: number = 0;
  private yuvPlayer: any = null;
  private gl: WebGLRenderingContext | null;
  constructor(url: string, container: HTMLCanvasElement, options) {
    this.url = url;
    this.container = container;

    //初始化下载线程
    this.downloadWorker = new Worker(
      new URL("./downloader.ts", import.meta.url)
    );

    this.downloadWorker.postMessage({
      u: this.url,
    });
    this.downloadWorker.onmessage = (evt) => {
      var objData = evt.data;
      switch (objData.t) {
        case 0:
          this.onGetFileInfo(objData.i);
          break;
        case 1:
          this.getFileData(objData.d);
          break;
      }
    };

    //初始化解线程
    this.decodeWorker = new Worker(new URL("./decoder.ts", import.meta.url));
    this.decodeWorker.onmessage = (event) => {
      var objData = event.data;
      switch (objData.t) {
        case 0:
          this.onInitDecoder(objData);
          break;
        case 2:
          this.onOpenDecoder(objData);
          break;
        case 4:
          this.onVideoFrame(objData);
          break;
        case 8:
          this.onDecodeFinished(objData);
          break;
        case 9:
          this.onRequestData(objData.o, objData.a);
          break;
      }
    };

    this.gl = this.container.getContext("webgl");
    this.initWebGL();
  }

  //初始化webgl
  initWebGL() {
    let gl = this.gl as WebGLRenderingContext;
    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
    var program: WebGLProgram | null = gl.createProgram();

    if (!program) {
      return;
    }

    var vertexShaderSource = [
      "attribute highp vec4 aVertexPosition;",
      "attribute vec2 aTextureCoord;",
      "varying highp vec2 vTextureCoord;",
      "void main(void) {",
      " gl_Position = aVertexPosition;",
      " vTextureCoord = aTextureCoord;",
      "}",
    ].join("\n");
    let vertexShader: WebGLShader | null = gl.createShader(gl.VERTEX_SHADER);
    if (!vertexShader) {
      return;
    }
    gl.shaderSource(vertexShader, vertexShaderSource);
    gl.compileShader(vertexShader);
    var fragmentShaderSource = [
      "precision highp float;",
      "varying lowp vec2 vTextureCoord;",
      "uniform sampler2D YTexture;",
      "uniform sampler2D UTexture;",
      "uniform sampler2D VTexture;",
      "const mat4 YUV2RGB = mat4",
      "(",
      " 1.1643828125, 0, 1.59602734375, -.87078515625,",
      " 1.1643828125, -.39176171875, -.81296875, .52959375,",
      " 1.1643828125, 2.017234375, 0, -1.081390625,",
      " 0, 0, 0, 1",
      ");",
      "void main(void) {",
      " gl_FragColor = vec4( texture2D(YTexture, vTextureCoord).x, texture2D(UTexture, vTextureCoord).x, texture2D(VTexture, vTextureCoord).x, 1) * YUV2RGB;",
      "}",
    ].join("\n");

    var fragmentShader: WebGLShader | null = gl.createShader(
      gl.FRAGMENT_SHADER
    );
    if (!fragmentShader) {
      return;
    }

    gl.shaderSource(fragmentShader, fragmentShaderSource);
    gl.compileShader(fragmentShader as WebGLShader);
    gl.attachShader(program as WebGLProgram, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    gl.useProgram(program);
    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
      console.log("[ER] Shader link failed.");
    }
    var vertexPositionAttribute = gl.getAttribLocation(
      program,
      "aVertexPosition"
    );
    gl.enableVertexAttribArray(vertexPositionAttribute);
    var textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord");
    gl.enableVertexAttribArray(textureCoordAttribute);

    var verticesBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, verticesBuffer);
    gl.bufferData(
      gl.ARRAY_BUFFER,
      new Float32Array([
        1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0,
      ]),
      gl.STATIC_DRAW
    );
    gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
    var texCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
    gl.bufferData(
      gl.ARRAY_BUFFER,
      new Float32Array([1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0]),
      gl.STATIC_DRAW
    );
    gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);

    gl.y = new Texture(gl);
    gl.u = new Texture(gl);
    gl.v = new Texture(gl);
    gl.y.bind(0, program, "YTexture");
    gl.u.bind(1, program, "UTexture");
    gl.v.bind(2, program, "VTexture");
  }

  //获取视频基础信息（时长，文件大小等），
  onGetFileInfo(data: any) {
    console.log(data);
    let obj = {
      t: 0,
      s: Number(data.sz),
      c: 65536,
    };

    console.log(obj);

    // 初始化解码器
    this.decodeWorker.postMessage(obj);
  }

  //获取视频媒体信息，发送给渲染进程
  getFileData(data: ArrayBuffer) {
    console.log(77777777);
    this.decodeWorker.postMessage({
      t: 4,
      d: data,
    });
  }

  //播放
  play() {
    //获取视频基础信息
    this.downloadWorker.postMessage({
      t: 0,
      u: this.url,
    });
  }

  //暂停
  pause() {}

  //渲染帧
  renderFrame(frame: Uint8Array) {
    if (this.decoding) {
      return;
    }

    var gl = this.gl;
    if (!gl) {
      return;
    }
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    gl.clearColor(0.0, 0.0, 0.0, 0.0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    gl.y.fill(this.width, this.height, frame.subarray(0, this.yLength));
    gl.u.fill(
      this.width >> 1,
      this.height >> 1,
      frame.subarray(this.yLength, this.yLength + this.uvLength)
    );
    gl.v.fill(
      this.width >> 1,
      this.height >> 1,
      frame.subarray(this.yLength + this.uvLength, frame.length)
    );

    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  }

  //下载文件
  downloadChunk() {
    this.downloadWorker.postMessage({
      t: 1,
      s: 65536 * this.step,
      end: 65536 * (this.step + 1),
    });

    this.step++;

    if (this.step < 10) {
      this.downloadChunk();
    }
  }

  // 解码器初始化回调
  onInitDecoder(data: any) {
    console.log(222222, data);

    setTimeout(() => {
      this.decodeWorker.postMessage({
        t: 2,
      });
    }, 5000);
    this.downloadChunk();
  }
  // 打开解码器回调
  onOpenDecoder(data: any) {
    console.log(333333333, data);
    this.height = data.v.h;
    this.width = data.v.w;

    this.yLength = this.height * this.width;
    this.uvLength = (this.height * this.width) / 4;

    this.decodeWorker.postMessage({
      t: 5,
      i: 0,
    });
  }
  // 视频帧回调
  onVideoFrame(data: any) {
    console.log(565656656, data);
    this.renderFrame(data.d);
  }
  // 音频帧回调
  onAudioFrame(data: any) {}
  // 解码完成回调
  onDecodeFinished(data: any) {}
  // 请求数据回调
  onRequestData(offset: number, size: number) {}
}
export default Player;
